/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package TimeRecording.tables;

import TimeRecording.business.DateRange;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

/**
 *
 * @author david
 */
@Entity
@Table(name = "clocked_period")
@NamedQueries({
    @NamedQuery(name = "ClockedPeriod.findAll", 
            query = "SELECT c " +
                    "FROM ClockedPeriod c"),
    @NamedQuery(name = "ClockedPeriod.findByBadgeId",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.clockedPeriodPK.badgeId = :badgeId"),
    @NamedQuery(name = "ClockedPeriod.findByEmployeeId",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.employeeId = :employeeId"),
    @NamedQuery(name = "ClockedPeriod.findByClockedIn",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.clockedPeriodPK.clockedIn = :clockedIn"),
    @NamedQuery(name = "ClockedPeriod.findByClockedOut",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.clockedOut = :clockedOut"),
    @NamedQuery(name = "ClockedPeriod.findBySyntheticIn",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.syntheticIn = :syntheticIn"),
    @NamedQuery(name = "ClockedPeriod.findBySyntheticOut",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.syntheticOut = :syntheticOut"),
    @NamedQuery(name = "ClockedPeriod.findByEmployeeIdAndDates",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.employeeId = :employeeId AND " +
                          "c.clockedPeriodPK.clockedIn >= :startDate AND " +
                          "c.clockedPeriodPK.clockedIn <= :endDate"),
    @NamedQuery(name = "ClockedPeriod.findMissingEmployee",
            query = "SELECT c " +
                    "FROM ClockedPeriod c " +
                    "WHERE c.employeeId IS NULL AND " +
                          "c.clockedPeriodPK.clockedIn >= :startDate")})

public class ClockedPeriod implements Serializable {
    
    private static final long serialVersionUID = 1L;

    // <editor-fold defaultstate="collapsed" desc="Property Change Support">
    @Transient
    private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(listener);
    } // </editor-fold>

    @EmbeddedId
    protected ClockedPeriodPK clockedPeriodPK;

    @Column(name = "clocked_out")
    @Temporal(TemporalType.TIMESTAMP)
    private Date clockedOut;

    @Basic(optional = false)
    @Column(name = "synthetic_in")
    private boolean syntheticIn;

    @Basic(optional = false)
    @Column(name = "synthetic_out")
    private boolean syntheticOut;

    @JoinColumn(name = "employee_id", referencedColumnName = "employee_id")
    @ManyToOne
    private Employee employeeId;

    public ClockedPeriod() {
    }

    public ClockedPeriod(ClockedPeriodPK clockedPeriodPK) {
        this.clockedPeriodPK = clockedPeriodPK;
    }

    public ClockedPeriod(ClockedPeriodPK clockedPeriodPK, boolean syntheticIn, boolean syntheticOut) {
        this.clockedPeriodPK = clockedPeriodPK;
        this.syntheticIn = syntheticIn;
        this.syntheticOut = syntheticOut;
    }

    public ClockedPeriod(Integer employeeId, Date clockedIn) {
        this.clockedPeriodPK = new ClockedPeriodPK(employeeId, clockedIn);
    }

    public ClockedPeriodPK getClockedPeriodPK() {
        return clockedPeriodPK;
    }

    public void setClockedPeriodPK(ClockedPeriodPK clockedPeriodPK) {
        ClockedPeriodPK oldClockedPeriodPK = this.clockedPeriodPK;
        this.clockedPeriodPK = clockedPeriodPK;
        changeSupport.firePropertyChange("clockedPeriodPK",
                oldClockedPeriodPK, clockedPeriodPK);
    }

    public Date getClockedOut() {
        return clockedOut;
    }

    public void setClockedOut(Date clockedOut) {
        Date oldClockedOut = this.clockedOut;
        this.clockedOut = clockedOut;
        changeSupport.firePropertyChange("clockedOut", oldClockedOut, clockedOut);
    }

    public boolean getSyntheticIn() {
        return syntheticIn;
    }

    public void setSyntheticIn(boolean syntheticIn) {
        boolean oldSyntheticIn = this.syntheticIn;
        this.syntheticIn = syntheticIn;
        changeSupport.firePropertyChange("syntheticIn", oldSyntheticIn, syntheticIn);
    }

    public boolean getSyntheticOut() {
        return syntheticOut;
    }

    public void setSyntheticOut(boolean syntheticOut) {
        boolean oldSyntheticOut = this.syntheticOut;
        this.syntheticOut = syntheticOut;
        changeSupport.firePropertyChange("syntheticOut", oldSyntheticOut, syntheticOut);
    }

    public Employee getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(Employee employeeId) {
        Employee oldEmployeeId = this.employeeId;
        this.employeeId = employeeId;
        changeSupport.firePropertyChange("employeeId", oldEmployeeId, employeeId);
    }

    public double getDuration() {
        if (clockedOut == null) {
            return 0.0;
        } else {
            long millis = getClockedOut().getTime() -
                    clockedPeriodPK.getClockedIn().getTime();
            return (double)millis /(1000 * 60 * 60);
        }
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (clockedPeriodPK != null ? clockedPeriodPK.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof ClockedPeriod)) {
            return false;
        }
        ClockedPeriod other = (ClockedPeriod) object;
        if ((this.clockedPeriodPK == null && other.clockedPeriodPK != null) || (this.clockedPeriodPK != null && !this.clockedPeriodPK.equals(other.clockedPeriodPK))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "TimeRecording.tables.ClockedPeriod[clockedPeriodPK=" + clockedPeriodPK + "]";
    }

    /**
     * Simply get all clocking entries for the period of time for the specified
     * employee.  Time/Date selection is driven purely from the clocked in date.
     * It is assumed the granularity of requests will be on whole days and we
     * never expect to see a clocked period extend across the midnight barrier.
     *
     * Important: The end date will typically specify the hours, minutes and
     * seconds right up to the end of the day.
     *
     * @param entityManager connection to the persistence layer
     * @param employeeId database ID of the employee of interest
     * @param startDate start of period (see method description)
     * @param endDate end of period (see method description)
     * @return
     */
    public static List<ClockedPeriod> getClockedPeriodsForEmployeeAndPeriod(
            EntityManager entityManager, Employee employee, DateRange dateRange) {

        Query query = entityManager.createNamedQuery(
                "ClockedPeriod.findByEmployeeIdAndDates").
                setParameter("employeeId", employee).
                setParameter("startDate", dateRange.getFromDate()).
                setParameter("endDate", dateRange.getToDate());

        return query.getResultList();
    }

    /**
     * This method will return clocked periods that fall after the supplied date
     * that are not yet associated with an employee record.
     *
     * @param entityManager connection to the persistence layer
     * @param startDate the date after which records are to be returned
     * @return a list of clocked periods
     */
    public static List<ClockedPeriod> getClockedPeriodsMissingAnEmployee(
            EntityManager entityManager, Date startDate) {

        Query query = entityManager.createNamedQuery(
                "ClockedPeriod.findMissingEmployee").
                setParameter("startDate", startDate);

        return query.getResultList();
    }
}